package cn.hiapi.user.api;

import cn.hiapi.basic.ResponseEntity;
import cn.hiapi.basic.controller.BasicQueryController;
import cn.hiapi.basic.entity.BasicEntity;
import cn.hiapi.basic.exception.BasicException;
import cn.hiapi.basic.jpa.pageable.QueryWrapper;
import cn.hiapi.basic.pay.consts.PayConst;
import cn.hiapi.basic.redis.data.LockConst;
import cn.hiapi.basic.redis.service.RedisService;
import cn.hiapi.basic.security.TokenGet;
import cn.hiapi.basic.token.Token;
import cn.hiapi.basic.token.enums.TokenType;
import cn.hiapi.interfaces.DispatchContext;
import cn.hiapi.interfaces.ILockService;
import cn.hiapi.interfaces.config.IConfigService;
import cn.hiapi.interfaces.finance.IAmountService;
import cn.hiapi.interfaces.finance.IAssetsService;
import cn.hiapi.interfaces.finance.IPaymentService;
import cn.hiapi.interfaces.finance.model.*;
import cn.hiapi.shop.entity.shop.Shop;
import cn.hiapi.shop.service.shop.ShopService;
import cn.hiapi.shop.vo.ShopBase;
import cn.hiapi.shopkeeper.consts.AppConst;
import cn.hiapi.shopkeeper.entity.activity.*;
import cn.hiapi.shopkeeper.entity.order.ActivityGroupOrder;
import cn.hiapi.shopkeeper.entity.order.ActivityOrder;
import cn.hiapi.shopkeeper.entity.order.ActivityOrderLog;
import cn.hiapi.shopkeeper.enums.ActivityType;
import cn.hiapi.shopkeeper.enums.OrderLogType;
import cn.hiapi.shopkeeper.enums.OrderStatus;
import cn.hiapi.shopkeeper.service.Shopkeeper;
import cn.hiapi.shopkeeper.service.activity.AbsActivityService;
import cn.hiapi.shopkeeper.service.activity.ActivityCodeRecordService;
import cn.hiapi.shopkeeper.service.activity.ActivityFactory;
import cn.hiapi.shopkeeper.service.activity.ActivityService;
import cn.hiapi.shopkeeper.service.order.ActivityGroupOrderService;
import cn.hiapi.shopkeeper.service.order.ActivityOrderLogService;
import cn.hiapi.shopkeeper.service.order.ActivityOrderService;
import cn.hiapi.shopkeeper.service.user.SubscribeShopService;
import cn.hiapi.user.entity.InviteCode;
import cn.hiapi.user.request.GiftOrderRequest;
import cn.hiapi.user.api.request.RepayOrderRequest;
import cn.hiapi.user.entity.User;
import cn.hiapi.user.query.ActivityOrderQuery;
import cn.hiapi.user.request.CancelOrderRequest;
import cn.hiapi.shopkeeper.request.CreateOrderRequest;
import cn.hiapi.user.service.InviteCodeService;
import cn.hiapi.user.service.UserService;
import cn.hiapi.user.service.WxInfoService;
import cn.hiapi.user.vo.ActivityOrderGroupVo;
import cn.hiapi.user.vo.ActivityOrderVo;
import cn.hiapi.utils.JsonUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Maps;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author AdinZ
 * @date 2022/12/17 13:39
 */
@RestController("shopkeeperUserOrderController")
@RequestMapping("/user/order")
public class OrderController extends BasicQueryController<ActivityOrder, Long, ActivityOrderVo, ActivityOrderQuery> {

    private final ActivityOrderService basicService;

    protected OrderController(ActivityOrderService basicService, DispatchContext dispatchContext) {
        super(basicService, dispatchContext);
        this.basicService = basicService;
    }

    @ApiOperation(value = "创建订单入口", notes = "创建订单入口", nickname = "AdinZ")
    @PostMapping("/create")
    @Transactional
    public ResponseEntity createOrder(@RequestBody CreateOrderRequest request) {
        Token<Object> token = TokenGet.get();

        final Map<String, Long> incrementStock = Maps.newConcurrentMap();
        String lock = LockConst.getLock("order-create", token.getFid());
        final ActivityOrder order;

        try {
            this.dispatchContext.getServiceOne(ILockService.class).lockThr(lock);
            Activity activity = this.dispatchContext.getServiceOne(ActivityService.class).get(request.getId());
            if (activity == null) return toError("活动不存在");
            AbsActivityService service = this.dispatchContext.getServiceOne(ActivityFactory.class).get(activity.getType());
            if (service == null) return toError("活动类型无法下单");
            final long now = System.currentTimeMillis();

            String status = activity.verifyStatus();
            if (StringUtils.isNotEmpty(status)) {
                return toError(status);
            }


            if (request.getSugar() != null && request.getSugar().compareTo(BigDecimal.ZERO) > 0) {


            }

            BasicEntity basic = service.getByAid(activity.getId());
            if (basic == null) throw new BasicException("活动详情为空");

            order = service.createOrder(basic, activity, incrementStock, request, token);
            order.setExpandText(basic.toJSONString());

            if (order == null) throw new BasicException("类型错误");


            if (request.getSugar() != null && request.getSugar().compareTo(BigDecimal.ZERO) > 0) {
                JSONObject config = this.dispatchContext.getServiceOne(IConfigService.class).getConfig(AppConst.CONFIG_SHOPKEEPER_CONFIG);
                BigDecimal rate = JsonUtils.getBigDecimal(config, AppConst.CONFIG_SHOPKEEPER_CONFIG_SUGAR_RATE);
                order.setSugar(Shopkeeper.moneyToSugar(request.getSugar(), rate));
                AmountModel model = this.dispatchContext.getServiceOne(IAmountService.class).getByFid(token.getFid(), AppConst.FINANCE_AMOUNT_SUGAR);
                if (model.getAmount().compareTo(order.getSugar()) < 0) {
                    return toError("喜糖不足");
                }
                order.setDiscount(request.getSugar());
            }
            if (StringUtils.isNotEmpty(request.getInviteCode())) {
                InviteCode code = this.dispatchContext.getServiceOne(InviteCodeService.class).inviteGet(request.getInviteCode());
                if (code != null) {
                    order.setInviteUid(code.getUid());
                }
            }

            order.setBuyerId(token.getFid());
            order.setTitle(activity.getTitle());
            order.setThumb(activity.getThumb());
            order.setAid(activity.getId());
            order.setShopId(activity.getShopId());
            order.setActivityType(activity.getType());
            Shop shop = this.dispatchContext.getServiceOne(ShopService.class).get(activity.getShopId());
            if (shop != null) order.setSellerId(shop.getUid());
            order.setBuyerRemark(request.getRemark());
            order.setUseStart(activity.getUseStart());
            order.setUseEnd(activity.getUseEnd());
            order.calculate();
            order.setPayType(request.getPayType());
            //未支付关闭订单时间
            JSONObject config = this.dispatchContext.getServiceOne(IConfigService.class).getConfig(AppConst.CONFIG_SHOPKEEPER_ACTIVITY);
            long timeout = config.getLongValue(AppConst.CONFIG_SHOPKEEPER_ACTIVITY_ORDER_TIMEOUT);
            order.setExpire(now + (timeout * 60 * 1000l));

            this.dispatchContext.getServiceOne(ActivityOrderService.class).save(order);

            return this.orderPay(token, order, request.getPayPwd());
        } finally {
            incrementStock.forEach((key, value) -> this.dispatchContext.getServiceOne(RedisService.class).increment(AppConst.ACTIVITY_SELL_STOCK_KEY, value, -1));
            this.dispatchContext.getServiceOne(ILockService.class).unlock(lock);
        }
    }


    @Override
    public ActivityOrderQuery setQuery(ActivityOrderQuery query) {
        query.setBuyerId(TokenGet.get().getFid());
        return super.setQuery(query);
    }

    @Override
    public List<ActivityOrderVo> toListVo(List<ActivityOrder> list, String[] fields) {
        return list.stream().map(e -> this.toBase(e))
                .collect(Collectors.toList());
    }

    @Override
    public ActivityOrderVo toData(ActivityOrder data, String[] fields) {
        Token<Object> token = TokenGet.get();
        if (token.getFid() != data.getBuyerId()) throw new BasicException("订单不是我的!");
        ActivityOrderVo vo = this.toBase(data);

        return vo;
    }


    private ActivityOrderVo toBase(ActivityOrder data) {
        ActivityOrderVo vo = data.copy(new ActivityOrderVo());
        Shop shop = this.dispatchContext.getServiceOne(ShopService.class).get(data.getShopId());
        if (shop != null)
            vo.setShop(shop.copy(new ShopBase()));

        if (data.getActivityType() == ActivityType.GROUP) {
            ActivityOrderGroupVo group = new ActivityOrderGroupVo();
            if (data.getFid() != null) {
                group.setItems(basicService.groupUserList(data.getFid(), data.getStatus()));
                ActivityGroupOrder order = this.dispatchContext.getServiceOne(ActivityGroupOrderService.class).get(data.getFid());
                if (order != null) {
                    group.setGroupNum(order.getGroupNum());
                    group.setExpire(order.getExpire());
                }
                group.setId(order.getId());
                vo.setGroup(group);
            }


        } else if (data.getActivityType() == ActivityType.CARD) {
            JSONObject object;
            if (!StringUtils.isEmpty(data.getExpandText())) {
                object = JSON.parseObject(data.getExpandText());
            } else {
                BasicEntity entity = this.dispatchContext.getServiceOne(ActivityFactory.class).get(data.getActivityType()).getByAid(data.getAid());
                object = JSON.parseObject(entity.toJSONString());
            }
            List<JSONObject> card = this.dispatchContext.getServiceOne(ActivityCodeRecordService.class).totalCount(data.getId());
            JSONObject cardObj = JsonUtils.toObject(card, e -> JsonUtils.builder().put("status_" + e.getIntValue("status"), e.getIntValue("count")).build());
            object.put("codeTotal", cardObj);
            vo.setExpand(object);
        }
        if (data.getActivityType() != ActivityType.CARD) {
            List<ActivityCodeRecord> recordList = this.dispatchContext.getServiceOne(ActivityCodeRecordService.class).findByOid(data.getId());
            List<JSONObject> collect = recordList.stream().map(e -> JsonUtils.builder().put("oid", e.getOid())
                    .put("status", e.getStatus())
                    .put("created", e.getCreated())
                    .put("update", e.getUpdated())
                    .put("code", e.getCode()).build()).collect(Collectors.toList());
            vo.setCodes(collect);
        }
        return vo;
    }


    @ApiOperation(value = "取消订单", notes = "取消订单", nickname = "AdinZ")
    @PostMapping("/cancel")
    public ResponseEntity cancelOrder(@RequestBody CancelOrderRequest request) {
        Token<Object> token = TokenGet.get();
        ActivityOrder order = basicService.get(request.getId());
        if (order == null || token.getFid() != order.getBuyerId()) {
            return toError("订单不存在");
        }
        if (order.getStatus() != OrderStatus.WAIT_PAY) {
            return toError("订单状态错误");
        }
        this.basicService.closeOrder(order);

        ActivityOrderLog t = new ActivityOrderLog();
        t.setOid(order.getId());
        t.setUid(token.getFid());
        t.setUserType(token.getType().name());
        t.setRemark(request.getRemark());
        t.setType(OrderLogType.CANCEL);
        this.dispatchContext.getServiceOne(ActivityOrderLogService.class).save(t);

        return toSuccess();
    }

    @ApiOperation(value = "重新支付订单", notes = "重新支付订单", nickname = "AdinZ")
    @PostMapping("/repay")
    public ResponseEntity repayOrder(@RequestBody RepayOrderRequest request) {
        Token<Object> token = TokenGet.get();
        ActivityOrder order = this.basicService.get(request.getId());
        if (order == null || token.getFid() != order.getBuyerId()) return toError("订单不存在");

        if (order.getStatus() != OrderStatus.WAIT_PAY) return toError("订单状态错误");

        if (order.getExpire() <= System.currentTimeMillis() - 2000) {
            return toError("订单超时关闭了");
        }

        order.setPayType(request.getPayType());
        //未支付关闭订单时间
        JSONObject config = this.dispatchContext.getServiceOne(IConfigService.class).getConfig(AppConst.CONFIG_SHOPKEEPER_ACTIVITY);
        long timeout = config.getLongValue(AppConst.CONFIG_SHOPKEEPER_ACTIVITY_ORDER_TIMEOUT);
        order.setExpire(System.currentTimeMillis() + (timeout * 60 * 1000l));
        return this.orderPay(token, order, request.getPayPwd());
    }


    @PostMapping("/gift")
    @ApiOperation(value = "订单转移给好友", notes = "订单转移给好友", nickname = "AdinZ")
    public ResponseEntity giftOrder(@RequestBody GiftOrderRequest request) {
        Token<User> token = TokenGet.get();
        ActivityOrder order = this.basicService.get(request.getId());

        if (order == null || token.getFid() != order.getBuyerId()) return toError("订单不存在");
        if (order.getStatus() != OrderStatus.WAIT_USE) return toError("订单已经使用无法转移");
        Long giftUid = this.dispatchContext.getServiceOne(UserService.class).getUid(token.getMerchantId(), request.getMobile());
        if (giftUid == null) return toError(request.getMobile() + "用户不存在");


        if (giftUid.longValue() == token.getFid()) return toError("不能转移给自己");
        ActivityOrder giftOrder = order.copy(new ActivityOrder());
        giftOrder.setBuyerId(giftUid);
        giftOrder.setUpdated(System.currentTimeMillis());
        giftOrder.setId(null);
        giftOrder.setGiftDirection(2);
        giftOrder.setGiftUid(token.getFid());
        this.basicService.save(giftOrder);

        this.dispatchContext.getServiceOne(ActivityCodeRecordService.class).giftOrder(order.getId(), giftOrder.getId());

        order.setStatus(OrderStatus.GIFT);
        order.setUpdated(System.currentTimeMillis());
        order.setGiftDirection(1);
        order.setGiftUid(giftUid);
        this.basicService.save(order);

        ActivityOrderLog t = new ActivityOrderLog();
        t.setOid(order.getId());
        t.setUid(token.getFid());
        t.setUserType(token.getType().name());
        t.setRemark(token.getData().getMobile() + "将订单转移给" + request.getMobile());
        t.setSender(token.getFid());
        t.setReceive(giftUid);
        t.setType(OrderLogType.GIFT);
        this.dispatchContext.getServiceOne(ActivityOrderLogService.class).save(t);
        return toSuccess();
    }


    @GetMapping("/delete")
    public ResponseEntity deleteOrder(@RequestParam("id") Long id) {
        Token<Object> token = TokenGet.get();

        ActivityOrder order = basicService.get(id);
        if (order == null || order.getBuyerId() != token.getFid())
            return toError("订单不存在");
        order.setDelStatus(1);
        order.setUpdated(System.currentTimeMillis());
        basicService.save(order);
        return toSuccess();
    }


    @Transactional
    ResponseEntity orderPay(Token token, ActivityOrder order, String payPwd) {

        PayTypes types;
        switch (order.getPayType()) {
            case AppConst.FINANCE_AMOUNT_MONEY:
                types = PayTypes.WALLET;
                break;
            case "weixin":
                types = PayTypes.WXPAY_MINI;
                break;
            default:
                throw new BasicException("支付类型错误");
        }
        if (order.getSugar().compareTo(BigDecimal.ZERO) > 0) {
            this.dispatchContext.getServiceOne(IAssetsService.class).changeAmount(
                    AmountChange.builder()
                            .fid(order.getBuyerId()).fidType(TokenType.ROLE_USER).mid(0)
                            .type(AppConst.FINANCE_AMOUNT_SUGAR).amount(order.getSugar().negate())
                            .sourceId(order.getId()).sourceType(AppConst.PAY_CALL_TYPE_ORDER)
                            .remark(order.getTitle() + " - 订单支付").title(order.getTitle() + " - 订单支付")
                            .build()
            );
        }


        RechargeModel.RechargeModelBuilder modelBuilder = RechargeModel.builder()
                .fid(order.getId())
                .fidType(token.getType().name())
                .uid(order.getBuyerId())
                .type(AppConst.PAY_CALL_TYPE_ORDER)
                .amountType(AppConst.FINANCE_AMOUNT_MONEY)
                .money(order.getPayment())
                .actual(order.getPayment())
                .title(order.getTitle() + " - 订单支付")
                .payType(types == PayTypes.WALLET ? "money" : "shopkeeper_user_wx_pay:" + PayConst.WXPAY_PROGRAM)
                .payPwd(payPwd);
        Map map = this.dispatchContext.getServiceOne(IPaymentService.class).createPayment(modelBuilder
                .build(), types);
        JsonUtils.Builder builder = JsonUtils.builder()
                .put("payment", order.getPayment())
                .put("created", order.getCreated())
                .put("payType", order.getPayType())
                .put("oid", order.getId());
        if (order.getActivityType() == ActivityType.GROUP) {
            builder.put("gid", order.getFid()).build();
        }
        if (map != null) {
            map.forEach((k, v) -> builder.put(k + "", v));
        }
        return toSuccess("支付成功", builder.build());
    }


    @ApiOperation(value = "待付款订单数", notes = "待付款订单数", nickname = "AdinZ")
    @GetMapping("/wait-pay")
    public ResponseEntity waitPayOrderCount() {
        Token<Object> token = TokenGet.get();
        long count = basicService.count(new QueryWrapper().eq("buyerId", token.getFid()).eq("status", OrderStatus.WAIT_PAY));
        long use = basicService.count(new QueryWrapper().eq("buyerId", token.getFid()).eq("status", OrderStatus.WAIT_USE));


        long subscribe = this.dispatchContext.getServiceOne(SubscribeShopService.class).count(new QueryWrapper().eq("uid", token.getFid()));
        return toSuccess(JsonUtils.builder()
                .put("wait", count)
                .put("subscribe", subscribe)
                .put("use", use).build());
    }


}
